/************************************************************************
* object.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#include "graphics.h"
#include "array.h"

#include <string.h>

static mesh_t *object_find_mat(object_t *o, material_t *mat)
{
	int i;
	mesh_t *m;
	for (i=0; i<o->meshes->length; i++) {
		m = ((mesh_t**)o->meshes->data)[i];
		if (m->mat && !strcmp(m->mat->name,mat->name))
			return m;
	}

	m = mesh_create_material(mat);

	array_push_ptr(o->meshes,m);

	return m;
}

static mesh_t *object_find_colour(object_t *o, colour_t *c)
{
	int i;
	mesh_t *m;
	for (i=0; i<o->meshes->length; i++) {
		m = ((mesh_t**)o->meshes->data)[i];
		if (m->col && m->col->r == c->r && m->col->g == c->g && m->col->b == c->b && m->col->a == c->a)
			return m;
	}

	m = mesh_create_colour(c);

	array_push_ptr(o->meshes,m);

	return m;
}

/* create a new 3d object */
object_t *object_create(v3_t *pos)
{
	object_t *o = NULL;
	return o;
	o->pos.x = pos->x;
	o->pos.y = pos->y;
	o->pos.z = pos->z;

	o->anim.skeleton = 0;
	o->anim.frame = 0;
	o->anim.value = 0.0;

	o->ignore = 0;
	o->drop = 0;

	o->bounds.max.x = 0.0;
	o->bounds.max.y = 0.0;
	o->bounds.max.z = 0.0;
	o->bounds.min.x = 0.0;
	o->bounds.min.y = 0.0;
	o->bounds.min.z = 0.0;

	return o;
}

/* set the rotation for a 3d object */
void object_rotate(object_t *o, GLfloat x, GLfloat y, GLfloat z)
{
	o->rot.x = x;
	o->rot.y = y;
	o->rot.z = z;
}

/* set the scale for a 3d object */
void object_scale(object_t *o, GLfloat x, GLfloat y, GLfloat z)
{
	o->scale.x = x;
	o->scale.y = y;
	o->scale.z = z;
}

/* add a material to a 3d object */
void object_add_material(object_t *o, material_t *mat)
{
	object_find_mat(o,mat);
}

/* add a colour to a 3d object */
void object_add_colour(object_t *o, colour_t *c)
{
	object_find_colour(o,c);
}

/* add a new poly to an object */
void object_add_mat_poly(object_t *o, material_t *mat, v3_t *v1, v3_t *v2, v3_t *v3)
{
	mesh_t *m = object_find_mat(o,mat);

	mesh_push_poly(m,v1,v2,v3);
}

/* add a new quad to an object */
void object_add_mat_quad(object_t *o, material_t *mat, v3_t *v1, v3_t *v2, v3_t *v3, v3_t *v4)
{
	mesh_t *m = object_find_mat(o,mat);

	mesh_push_quad(m,v1,v2,v3,v4);
}

/* set the skeleton for animation */
void object_set_skeleton(object_t *o, int skel)
{
	if (!o->m->skeletons || o->m->skeletons->length >= skel)
		o->anim.skeleton = 0;

	o->anim.skeleton = skel;
	o->anim.frame = 0;
	o->anim.value = 0.0;
}

/* calculate the object vertices for a given frame */
static void object_calc_frame(object_t *o, int frame)
{
	int i;
	skeleton_t *sk;
	frame_t* fr;

	array_t *fpre_current;

	mesh_t *m;

	sk = array_get_ptr(o->m->skeletons,o->anim.skeleton);
	if (!sk)
		sk = array_get_ptr(o->m->skeletons,0);
	fr = array_get_ptr(sk->frames,frame);
	if (!fr)
		fr = array_get_ptr(sk->frames,0);

	/* vertexes may be pre-calculated, if not do it now */
	if (!fr->pre_vertex) {
		int j;
		int k;
		int l;
		int wb;
		int we;

		weight_t *wt;
		joint_t *jt;
		mesh_t *bm;
		v3_t vt;
		v3_t v;

		fr->pre_vertex = array_create(ARRAY_TYPE_PTR);
		for (k=0; k<o->meshes->length; k++) {
			bm = array_get_ptr(o->m->meshes,k);
			fpre_current = array_create(ARRAY_TYPE_FLOAT);
			array_push_ptr(fr->pre_vertex,fpre_current);
			for (i=0; i<bm->m->length; i += 2) {
				v.x = 0.0;
				v.y = 0.0;
				v.z = 0.0;

				wb = array_get_int(bm->m,i);
				we = array_get_int(bm->m,i+1)+wb;

				/* Calculate final vertex to draw with weights */
				for (j=wb; j<we; j++) {
					wt = array_get_ptr(bm->w,j);
					jt = array_get_ptr(fr->joints,wt->joint);

					/* Calculate transformed vertex for this weight */
					quat_rotate(&jt->rot, &wt->pos, &vt);

					v.x += (jt->pos.x + vt.x) * wt->bias;
					v.y += (jt->pos.y + vt.y) * wt->bias;
					v.z += (jt->pos.z + vt.z) * wt->bias;
				}

				l = (i/2)*3;
				array_set_float(fpre_current,v.x,l);
				array_set_float(fpre_current,v.y,l+1);
				array_set_float(fpre_current,v.z,l+2);
			}
		}
	}

	/* now just get the frame data, and copy it to the object data */
	for (i=0; i<o->meshes->length; i++) {
		m = array_get_ptr(o->meshes,i);
		fpre_current = array_get_ptr(fr->pre_vertex,i);
		array_set_float(m->v,0.0,fpre_current->length);
		memcpy(m->v->data,fpre_current->data,sizeof(float)*fpre_current->length);
	}
}

/* set the frame for animation */
void object_set_frame(object_t *o, int frame)
{
	o->anim.frame = frame;
	o->anim.value = frame;
	object_calc_frame(o,frame);
}

/* advance an animated objects frame */
int object_advance_frame(object_t *o, float dtime)
{
	skeleton_t *sk = array_get_ptr(o->m->skeletons,o->anim.skeleton);
	if (!sk)
		sk = array_get_ptr(o->m->skeletons,0);

	o->anim.value += sk->fps*dtime;
	if (o->anim.frame == (int)o->anim.value)
		return 0;

	o->anim.frame = (int)o->anim.value;
	if (o->anim.frame >= sk->frames->length) {
		o->anim.value -= o->anim.frame;
		o->anim.frame = 0;
	}

	object_calc_frame(o,o->anim.frame);

	return 1;
}

/* calculate normals for an object */
void object_calc_normals(object_t *o)
{
	int i;
	mesh_t *m;
	for (i=0; i<o->meshes->length; i++) {
		m = ((mesh_t**)o->meshes->data)[i];
		mesh_calc_normals(m);
	}
}

/* calculate normals for an object */
void object_calc_bounds(object_t *o)
{
	v3_t min = {1000.0,1000.0,1000.0};
	v3_t max = {-1000.0,-1000.0,-1000.0};
	int i;
	int k;
	int l;
	mesh_t **m;
	v3_t *v;

	m = o->meshes->data;
	for (i=0; i<o->meshes->length; i++) {
		v = m[i]->v->data;
		l = m[i]->v->length/3;
		for (k=0; k<l; k++) {
			if (v[k].x < min.x)
				min.x = v[k].x;
			if (v[k].y < min.y)
				min.y = v[k].y;
			if (v[k].z < min.z)
				min.z = v[k].z;
			if (v[k].x > max.x)
				max.x = v[k].x;
			if (v[k].y > max.y)
				max.y = v[k].y;
			if (v[k].z > max.z)
				max.z = v[k].z;
		}
	}

	o->bounds.min.x = min.x*o->scale.x;
	o->bounds.min.y = min.y*o->scale.y;
	o->bounds.min.z = min.z*o->scale.z;
	o->bounds.max.x = max.x*o->scale.x;
	o->bounds.max.y = max.y*o->scale.y;
	o->bounds.max.z = max.z*o->scale.z;
}
